#ifndef __IM_MAIN_LOOP_H__
#define __IM_MAIN_LOOP_H__

#include <im_object.h>
#include <im_io_channel.h>
/**
 * @defgroup IMMainLoop
 * @ingroup PrivateIMBus
 * @{
 */

/** @struct IMMainLoop
 *  @brief Interface class of a main loop.
 *
 *  The purpose of IMMainLoop is to watch I/O events of multiple IMIOChannels
 *  and handle timeout callbacks.
 */

IM_BEGIN_DECLS

#define IM_MAIN_LOOP(p)             (IM_TYPE_INSTANCE_CHECK_CAST((p),IM_TYPE_MAIN_LOOP,IMMainLoop))
#define IM_CONST_MAIN_LOOP(p)       (IM_TYPE_INSTANCE_CHECK_CAST_CONST((p),IM_TYPE_MAIN_LOOP,IMMainLoop))
#define IM_MAIN_LOOP_CLASS(c)       (IM_TYPE_CLASS_CHECK_CAST((c),IM_TYPE_MAIN_LOOP,IMMainLoopClass))
#define IM_IS_MAIN_LOOP(p)          (IM_TYPE_INSTANCE_CHECK_TYPE((p),IM_TYPE_MAIN_LOOP))
#define IM_IS_MAIN_LOOP_CLASS(c)    (IM_TYPE_CLASS_CHECK_TYPE((c),IM_TYPE_MAIN_LOOP))
#define IM_MAIN_LOOP_GET_CLASS(p)   (IM_TYPE_INSTANCE_GET_CLASS_CAST((p),IM_TYPE_MAIN_LOOP,IMMainLoopClass))

typedef enum
{
    IM_WATCH_NONE        = 0,
    IM_WATCH_IO_READ     = 1,
    IM_WATCH_IO_WRITE    = 2,
    IM_WATCH_TIMEOUT     = 3
} IMWatchType;

typedef struct _IMMainLoop                    IMMainLoop;
typedef struct _IMMainLoopClass               IMMainLoopClass;

/**
 * @brief Function to handle watch events.
 *
 * @return The function should return FALSE if the Watch should
 *         be removed.
 */
typedef IMBool  (*IMWatchFunc)          (IMMainLoop     *loop,
                                         IMInt           watch_id,
                                         IMPointer       data);

struct _IMMainLoop
{
    IMInitiallyOwned parent_object;
};

struct _IMMainLoopClass
{
    IMInitiallyOwnedClass parent_class;

    IMInt   (*add_io_read_watch)        (IMMainLoop         *loop,
                                         IMIOChannel        *channel,
                                         IMWatchFunc         func,
                                         IMPointer           data,
                                         IMDestroyFunc       data_destroy_func);

    IMInt   (*add_io_write_watch)       (IMMainLoop         *loop,
                                         IMIOChannel        *channel,
                                         IMWatchFunc         func,
                                         IMPointer           data,
                                         IMDestroyFunc       data_destroy_func);

    IMInt   (*add_timeout_watch)        (IMMainLoop         *loop,
                                         IMInt               interval,
                                         IMWatchFunc         func,
                                         IMPointer           data,
                                         IMDestroyFunc       data_destroy_func);

    IMWatchType (*get_watch_type)       (IMMainLoop         *loop,
                                         IMInt               watch_id);

    IMIOChannel* (*get_watch_io_channel)(IMMainLoop         *loop,
                                         IMInt               watch_id);

    IMInt   (*get_watch_timeout)        (IMMainLoop         *loop,
                                         IMInt               watch_id);

    IMBool  (*remove_watch)             (IMMainLoop         *loop,
                                         IMInt               watch_id);

    void    (*run)                      (IMMainLoop         *loop);
    IMBool  (*iteration)                (IMMainLoop         *loop,
                                         IMBool              may_block);
    void    (*quit)                     (IMMainLoop         *loop);
    IMBool  (*is_running)               (IMMainLoop         *loop);
};

/**
 * @brief Create a new IMMainLoop object for a specified main loop type.
 *
 * The newly created IMMainLoop object is owned by the creator
 * (not floating). So creator should unref it explicitly when it's not
 * used anymore.
 */
IMMainLoop*     im_main_loop_new               (IMType           type);

/**
 * @brief add an i/o read watch for a specified IMIOChannel object.
 *
 * a reference of the IMIOChannel object will be owned by the IMMainLoop
 * object, until the watch is removed.
 *
 * The callback function will be called if IO read event is occurred
 * on the specified IMIOChannel object.
 * The watch will be removed, if the callback function returns FALSE.
 *
 * @return id of the watch, which can be used to remove the watch afterwards.
 *         -1 means failed to add the watch.
 */
IMInt           im_main_loop_add_io_read_watch  (IMMainLoop         *loop,
                                                 IMIOChannel        *channel,
                                                 IMWatchFunc         func,
                                                 IMPointer           data,
                                                 IMDestroyFunc       data_destroy_func);

/**
 * @brief add an i/o write watch for a specified IMIOChannel object.
 *
 * a reference of the IMIOChannel object will be owned by the IMMainLoop
 * object, until the watch is removed.
 *
 * The callback function will be called if IO write event is occurred
 * on the specified IMIOChannel object.
 * The watch will be removed, if the callback function returns FALSE.
 *
 * @return id of the watch, which can be used to remove the watch afterwards.
 *         -1 means failed to add the watch.
 */
IMInt           im_main_loop_add_io_write_watch (IMMainLoop         *loop,
                                                 IMIOChannel        *channel,
                                                 IMWatchFunc         func,
                                                 IMPointer           data,
                                                 IMDestroyFunc       data_destroy_func);

/**
 * @brief add an timeout watch for a specified timeout interval.
 *
 * The callback function will be called at interval of specified timeout.
 * The watch will be removed, if the callback function returns FALSE.
 *
 * @param interval A specified timeout interval, in milliseconds.
 * @return id of the watch, which can be used to remove the watch afterwards.
 *         -1 means failed to add the watch.
 */
IMInt           im_main_loop_add_timeout_watch  (IMMainLoop         *loop,
                                                 IMInt               interval,
                                                 IMWatchFunc         func,
                                                 IMPointer           data,
                                                 IMDestroyFunc       data_destroy_func);
/**
 * @brief Get type of a watch by its watch id.
 */
IMWatchType     im_main_loop_get_watch_type     (IMMainLoop         *loop,
                                                 IMInt               watch_id);

/**
 * @brief For a IO read or write watch, it returns the watched IMIOChannel.
 *
 * For timeout watch it returns 0.
 */
IMIOChannel*    im_main_loop_get_watch_io_channel(IMMainLoop         *loop,
                                                  IMInt               watch_id);

/**
 * @brief For a timeout watch, it returns the interval of the watch.
 *
 * For IO read or write watch, it returns -1.
 */
IMInt           im_main_loop_get_watch_timeout  (IMMainLoop         *loop,
                                                 IMInt               watch_id);

/**
 * @brief Remove a watch by its ID.
 */
IMBool          im_main_loop_remove_watch       (IMMainLoop         *loop,
                                                 IMInt               watch_id);

/**
 * @brief Run a main loop.
 *
 * This function won't return until the main loop is quit by calling
 * im_main_loop_quit().
 */
void            im_main_loop_run                (IMMainLoop         *loop);

/**
 * @brief Runs a single iteration for the given main loop.
 *
 * This involves checking to see if any event watches are ready to be
 * processed, then if no event watches are ready and may_block is TRUE,
 * waiting for watches to become ready, then dispatching the watches
 * that are ready. Note that even when may_block is TRUE, it is
 * still possible for im_main_loop_iteration () to return FALSE, since the the
 * wait may be interrupted for other reasons than an event watch becoming
 * ready.
 *
 * @return TRUE if one or more watch has been dispatched during this iteration.
 */
IMBool          im_main_loop_iteration          (IMMainLoop         *loop,
                                                 IMBool              may_block);

/**
 * @brief Quit a main loop.
 */
void            im_main_loop_quit               (IMMainLoop         *loop);

/**
 * @brief Check if a main loop is running.
 */
IMBool          im_main_loop_is_running         (IMMainLoop         *loop);

IM_END_DECLS
/** @} */

#endif
/*
vi:ts=4:nowrap:ai:expandtab
*/
